﻿using System;
using UnityEngine;
using UnityEngine.UI;

namespace IQIGame.Onigao.Game
{
    /// <summary>
    /// 对循环列表更上层适配封装，让业务用起来简单
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class LoopScrollAdapter<T> : IUIList where T : UIBaseNode, new()
    {
        private IExLoopScrollRect exLoopScrollRect { get; }

        private LoopScrollRectBase originLoopScrollRect { get; }

        // 必选项，刷新复用的item用
        private Action<T, int> callIUnitChanged;

        // 可选项，ScrollPosition位置变化时
        public LoopScrollRectBase.ScrollRectEvent onValueChanged { get => originLoopScrollRect.onValueChanged; set => originLoopScrollRect.onValueChanged = value; }

        // 可选项，一些特殊操作需要监听回池子，比如回池子的时候移除上面一个选中的GameObject。
        private Action<T> callUnitReturnPool;

        public float horizontalNormalizedPosition => originLoopScrollRect.horizontalNormalizedPosition;

        public float verticalNormalizedPosition => originLoopScrollRect.verticalNormalizedPosition;

        // 可选项，刷新复用的item用
        public int totalCount { get; private set; } = -1;
        public bool isInfiniteLoop { get; private set; } = false;
        private UIBaseWindow belongWindow = null;
        private GameObject gobjRoot = null;
        private bool isRootActive = false;

        private ListUnitMappingData<T> dataMapping;
        private ListUnitSelect<T> unitSelect;
        private ListUnitUpdate<T> unitUpdate;
        public UnitSelectType selectedType { get => unitSelect.selectedType; set => unitSelect.selectedType = value; }
        public int selectedIndex => unitSelect.selectedIndex;

        public LoopScrollAdapter(IExLoopScrollRect loopScrollRect, UIBaseWindow baseWindow)
        {
            loopScrollRect.eventCreateItemObject = OnCreateItemObject;
            loopScrollRect.eventReturnItemObject = OnReturnItemObject;
            loopScrollRect.eventItemDataChange = OnItemDataChange;

            this.exLoopScrollRect = loopScrollRect;
            this.originLoopScrollRect = loopScrollRect.loopScrollRect;
            this.belongWindow = baseWindow;

            this.gobjRoot = originLoopScrollRect.gameObject;
            this.isRootActive = this.gobjRoot.activeSelf;

            this.dataMapping = new ListUnitMappingData<T>(4);
        }

        public LoopScrollAdapter<T> BindUnitChangedCall(Action<T, int> funcChanged)
        {
            this.callIUnitChanged = funcChanged;
            return this;
        }

        public LoopScrollAdapter<T> BindUnitReturnPoolCall(Action<T> funcReturn)
        {
            this.callUnitReturnPool = funcReturn;
            return this;
        }

        public LoopScrollAdapter<T> InitUnitSelected(UnitSelectType selectedType, Action<int, T> funcSelected, Action<int, T> funcUnSelected = null)
        {
            return InitUnitSelected(new ListUnitSelect<T>.UnitSelectData()
            {
                selectedType = selectedType,
                callSelected = funcSelected,
                callUnSelected = funcUnSelected,
            });
        }

        public LoopScrollAdapter<T> InitUnitSelected(ListUnitSelect<T>.UnitSelectData data)
        {
            if (this.unitSelect == null)
            {
                this.unitSelect = new ListUnitSelect<T>(this.dataMapping);
                this.unitSelect.Initialize(data);
            }
            return this;
        }

        public LoopScrollAdapter<T> SetUnitMitiSelect(int maxSelected, Action funcFullWhenSelectOther = null)
        {
            if (this.unitSelect != null)
            {
                this.unitSelect.SetMitiSelect(maxSelected, funcFullWhenSelectOther);
            }
            return this;
        }

        public LoopScrollAdapter<T> InitUnitUpdate()
        {
            if (this.unitUpdate == null)
            {
                this.unitUpdate = new ListUnitUpdate<T>(this.dataMapping);
                this.unitUpdate.AddUpdate();
            }
            return this;
        }

        public LoopScrollAdapter<T> RefillCells(int totalCount, int startItem = 0, float contentOffset = 0)
        {
            this.SetActive(true);
            this.totalCount = totalCount;
            this.isInfiniteLoop = totalCount == -1;
            this.unitSelect?.ReIniteListUnits();
            this.originLoopScrollRect.totalCount = totalCount;
            this.originLoopScrollRect.RefillCells(startItem, contentOffset);
            this.unitSelect?.ComplateIniteListUnits();
            return this;
        }

        public LoopScrollAdapter<T> RefillCellsAndSelected(int totalCount, int indexSelected = 0, float contentOffset = 0)
        {
            this.RefillCells(totalCount, indexSelected, contentOffset);
            this.SetSelected(indexSelected);
            return this;
        }

        public LoopScrollAdapter<T> RefillCellsFromEnd(int totalCount, int endItem = 0, bool alignStart = false)
        {
            this.totalCount = totalCount;
            this.isInfiniteLoop = totalCount == -1;
            this.unitSelect?.ReIniteListUnits();
            this.originLoopScrollRect.totalCount = totalCount;
            this.originLoopScrollRect.RefillCellsFromEnd(endItem, alignStart);
            this.unitSelect?.ComplateIniteListUnits();
            return this;
        }

        /// <summary>
        /// 无限循环列表
        /// </summary>
        public LoopScrollAdapter<T> RefillCellsInfinite(int totalCount, int startItem = 0, float contentOffset = 0)
        {
            this.totalCount = totalCount;
            this.isInfiniteLoop = true;
            this.unitSelect?.ReIniteListUnits();
            this.originLoopScrollRect.totalCount = -1;
            this.originLoopScrollRect.RefillCells(startItem, contentOffset);
            this.unitSelect?.ComplateIniteListUnits();
            return this;
        }

        public LoopScrollAdapter<T> RefillCellsInfiniteAndSelected(int totalCount, int indexSelected = 0, float contentOffset = 0)
        {
            this.RefillCellsInfinite(totalCount, indexSelected, contentOffset);
            this.SetSelected(indexSelected);
            return this;
        }

        public LoopScrollAdapter<T> RefreshCells()
        {
            originLoopScrollRect.RefreshCells();
            return this;
        }

        public LoopScrollAdapter<T> ScrollToCell(int index, float speed)
        {
            originLoopScrollRect.ScrollToCell(index, speed);
            return this;
        }

        public LoopScrollAdapter<T> ScrollToCellWithinTime(int index, float time)
        {
            originLoopScrollRect.ScrollToCellWithinTime(index, time);
            return this;
        }

        public LoopScrollAdapter<T> StopMovement()
        {
            originLoopScrollRect.StopMovement();
            return this;
        }

        private LoopScrollAdapter<T> SetSelected(int index)
        {
            bool isSelected = index >= 0 && (this.isInfiniteLoop || index < this.totalCount);
            this.unitSelect?.SetSelected(index, isSelected);
            return this;
        }

        public void UnSelected(int index)
        {
            bool isArgValid = index >= 0 && (this.isInfiniteLoop || index < this.totalCount);
            if (isArgValid)
            {
                this.unitSelect?.SetSelected(index, false);
            }
        }

        public void UnSelected(GameObject gameObject)
        {
            this.unitSelect?.SetSelected(gameObject, false);
        }

        public void ResetSelected(int selectIndex)
        {
            this.ScrollToCellWithinTime(selectIndex, 0);
            this.SetSelected(selectIndex);
        }

        public T GetTEntity(int index)
        {
            return this.dataMapping.GetTEntity(index);
        }

        private void OnCreateItemObject(GameObject gobj)
        {
            var newitem = new T();
            newitem.Init(gobj, this.belongWindow);
            this.dataMapping.InitTUnitMapping(newitem);
        }

        private void OnReturnItemObject(GameObject gobj)
        {
            this.UnSelected(gobj);
            this.dataMapping.RemoveIndexMapping(gobj);

            if (callUnitReturnPool != null)
            {
                var item = this.dataMapping.GetTEntity(gobj);
                callUnitReturnPool.Invoke(item);
            }
        }

        private void OnItemDataChange(GameObject gobj, int index)
        {
            if (callIUnitChanged == null)
            {
                return;
            }

            var node = this.dataMapping.GetTEntity(gobj);

            int indexCurr = index;
            if (this.totalCount > 0)
            {
                while (indexCurr < 0)
                    indexCurr += this.totalCount;
                indexCurr = indexCurr % this.totalCount;
            }
            this.dataMapping.InitIndexMapping(node, indexCurr);
            this.callIUnitChanged.Invoke(node, indexCurr);
            this.OnInitSelected(node, indexCurr);
        }

        private void OnInitSelected(T node, int index)
        {
            this.unitSelect?.Refresh(node, index);
        }

        public void SetActive(bool isActive)
        {
            if (this.isRootActive != isActive)
            {
                this.isRootActive = isActive;
                this.gobjRoot?.SetActive(isActive);
            }
        }

        public void Clear()
        {
            this.callIUnitChanged = null;
            this.callUnitReturnPool = null;
            this.belongWindow = null;

            this.totalCount = -1;
            this.isInfiniteLoop = false;

            if (this.gobjRoot != null)
            {
                this.exLoopScrollRect.eventCreateItemObject = OnCreateItemObject;
                this.exLoopScrollRect.eventReturnItemObject = OnReturnItemObject;
                this.exLoopScrollRect.eventItemDataChange = OnItemDataChange;
            }
            this.gobjRoot = null;
            this.isRootActive = false;

            this.unitSelect?.Clear();
            this.unitSelect = null;
            this.dataMapping?.Clear();
            this.dataMapping = null;
            this.unitUpdate?.Clear();
            this.unitUpdate = null;
        }
    }
}
